import * as dataForge from 'data-forge';
import 'data-forge-indicators';
import 'data-forge-fs';
import {plot } from 'plot';
import "@plotex/render-image";
var Table = require("easy-table");
const fs = require("fs");
const moment = require("moment");
// sudo cnpm install electron@6 --save-dev
import {
  IStrategy,
  backtest,
  analyze,
  IBar,
  ITrade,
  computeEquityCurve,
  computeDrawdown,
  IAnalysis,
} from "../..";


interface MyBar extends IBar {
  sma: number;
  fast_l: number;
  slow_h: number;
  slow_l: number;
}

async function main() {
  console.log("Loading and preparing data.");

  let inputSeries = dataForge
    .readFileSync(__dirname + "/data/2020-01-01to2021-01-01.csv")
    .parseCSV()
    .parseDates("date", "D/MM/YYYY ")
    .parseFloats(["open", "high", "low", "close", "volume"])
    .setIndex("date") // Index so we can later merge on date.
    .renameSeries({ date: "time" });

  // The example data file is available in the 'data' sub-directory of this repo.
  // interface IStrategyModifications {
  //   entryRule?: EntryRuleFn<MyBar>;
  //   exitRule?: ExitRuleFn<MyBar>;
  //   stopLoss?: StopLossFn<MyBar>;
  //   trailingStopLoss?: StopLossFn<MyBar>;
  //   profitTarget?: ProfitTargetFn<MyBar>;
  // }
  console.log("Computing moving average indicator.");

  const fast_h = inputSeries
    .deflate((bar) => bar.high) // Extract closing price series.
    .ema(9); // 30 day moving average.

  inputSeries = inputSeries
    .withSeries("fast_h", fast_h) // Integrate moving average into data, indexed on date.
    .skip(9); // Skip blank sma entries.
  const fast_l = inputSeries
    .deflate((bar) => bar.low) // Extract closing price series.
    .ema(9); // 30 day moving average.

  inputSeries = inputSeries
    .withSeries("fast_l", fast_l) // Integrate moving average into data, indexed on date.
    .skip(9); // Skip blank sma entries.

  const slow_h = inputSeries
    .deflate((bar) => bar.high) // Extract closing price series.
    .ema(89); // 30 day moving average.

  inputSeries = inputSeries
    .withSeries("slow_h", slow_h) // Integrate moving average into data, indexed on date.
    .skip(89); // Skip blank sma entries.

  const slow_l = inputSeries
    .deflate((bar) => bar.low) // Extract closing price series.
    .ema(89); // 30 day moving average.

  inputSeries = inputSeries
    .withSeries("slow_l", slow_l) // Integrate moving average into data, indexed on date.
    .skip(89); // Skip blank sma entries.

  // This is a very simple and very naive mean reversion strategy:
  const strategy: IStrategy<MyBar> = {
    entryRule: (enterPosition, args) => {
      if (args.bar.fast_l > args.bar.slow_h) {
        enterPosition();
      }
    },

    exitRule: (exitPosition, args) => {
      if (args.bar.fast_l < args.bar.slow_l) {
        exitPosition();
      }
    },

    // stopLoss: (args) => {
    //   // Intrabar stop loss.
    //   return args.entryPrice * (5 / 100); // Stop out on 5% loss from entry price.
    // },
  };

  console.log("Backtesting...");

  // Backtest your strategy, then compute and print metrics:
  const trades = backtest(strategy, inputSeries);
  console.log("The backtest conducted " + trades.length + " trades!");

  new dataForge.DataFrame(trades)
    .transformSeries({
      entryTime: (d) => moment(d).format("YYYY/MM/DD"),
      exitTime: (d) => moment(d).format("YYYY/MM/DD"),
    })
    .asCSV()
    .writeFileSync(__dirname + "/output/trades.csv");

  console.log("Analyzing...");

  const startingCapital = 10000;
  const analysis: IAnalysis = analyze(startingCapital, trades);

  const analysisTable = new Table();

  for (const key of Object.keys(analysis)) {
    analysisTable.cell("Metric", key);
    analysisTable.cell("Value", analysis[key as keyof IAnalysis]);
    analysisTable.newRow();
  }

  const analysisOutput = analysisTable.toString();
  console.log(analysisOutput);
  const analysisOutputFilePath = __dirname + "/output/analysis.txt";
  fs.writeFileSync(analysisOutputFilePath, analysisOutput);
  console.log(">> " + analysisOutputFilePath);

  console.log("Plotting...");

  // Visualize the equity curve and drawdown chart for your backtest:
  const equityCurve = computeEquityCurve(startingCapital, trades);
  const equityCurveOutputFilePath = __dirname + "/output/my-equity-curve.png";
  console.log(equityCurve);
    await plot(equityCurve, {
    chartType: "area",
    y: { label: "Equity $" },
  }).renderImage(equityCurveOutputFilePath);
  console.log(">> " + equityCurveOutputFilePath);

  const equityCurvePctOutputFilePath =
    __dirname + "/output/my-equity-curve-pct.png";
  const equityPct = equityCurve.map(
    (v) => ((v - startingCapital) / startingCapital) * 100
  );
  await plot(equityPct, {
    chartType: "area",
    y: { label: "Equity %" },
  }).renderImage(equityCurvePctOutputFilePath);
  console.log(">> " + equityCurvePctOutputFilePath);

  const drawdown = computeDrawdown(startingCapital, trades);
  const drawdownOutputFilePath = __dirname + "/output/my-drawdown.png";
  await plot(drawdown, {
    chartType: "area",
    y: { label: "Drawdown $" },
  }).renderImage(drawdownOutputFilePath);
  console.log(">> " + drawdownOutputFilePath);

  const drawdownPctOutputFilePath = __dirname + "/output/my-drawdown-pct.png";
  const drawdownPct = drawdown.map((v) => (v / startingCapital) * 100);
  await plot(drawdownPct, {
    chartType: "area",
    y: { label: "Drawdown %" },
  }).renderImage(drawdownPctOutputFilePath);
  console.log(">> " + drawdownPctOutputFilePath);
}

main()
  .then(() => console.log("Finished"))
  .catch((err) => {
    console.error("An error occurred.");
    console.error((err && err.stack) || err);
  });
